package org.xx.b2d;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;

import static org.apache.commons.lang3.StringUtils.trimToEmpty;

/**
 * 使用`java swing`构建的世界。
 */
public class BasicWorld
        extends World {
    private final Logger logger = LoggerFactory.getLogger(BasicWorld.class);

    private JFrame frame;
    private SceneAdaptor viewport;

    public BasicWorld(
            int speed
    ) {
        super(speed);
    }

    @Override
    public String getTitle() {
        return this.frame.getTitle();
    }

    @Override
    public void setTitle(
            String title
    ) {
        this.frame.setTitle(trimToEmpty(title));
    }

    @Override
    public void show(
            DisplayParams displayParams
    ) {
        SwingUtilities.invokeLater(() -> {
            initFrame();

            showFrame(displayParams);
        });
    }

    @Override
    public void hide() {
        this.frame.setVisible(false);
    }

    @Override
    public void loop() {
        final var guard = new Thread(this::forever, "guard");
        guard.setDaemon(true);
        guard.start();
    }

    private void initFrame() {
        if (this.frame == null) {
            final var viewport = new SceneAdaptor();

            final var box = Box.createHorizontalBox();
            box.add(Box.createHorizontalGlue());
            box.add(viewport);
            box.add(Box.createHorizontalGlue());

            final var frame = new JFrame();
            frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            frame.setResizable(false);
            frame.getRootPane().setBackground(Color.BLACK);
            frame.setContentPane(box);

            this.frame = frame;
            this.viewport = viewport;
        }
    }

    private void setViewportSize(
            WorldSize size
    ) {
        final var vSize = new Dimension(size.getWidth(), size.getHeight());
        this.viewport.setPreferredSize(vSize);
        this.viewport.setMinimumSize(vSize);
        this.viewport.setMaximumSize(vSize);
    }

    private void showFrame(
            DisplayParams displayParams
    ) {
        if (!this.frame.isVisible()) {
            logger.info("Show world: {}", displayParams);

            this.frame.setTitle(this.getTitle());

            if (!displayParams.isFullScreen()) {
                setViewportSize(displayParams.getSize());

                this.frame.setLocationByPlatform(true);
                this.frame.pack();
                this.frame.setVisible(true);
            } else {
                setViewportSize(DeviceUtils.getBestSizeByFullScreen(displayParams.getScreenId()));

                this.frame.setUndecorated(true);
                DeviceUtils.getScreen(displayParams.getScreenId()).setFullScreenWindow(this.frame);
            }
        }
    }

    protected void forever() {
        var lastTimestamp = 0L;
        this.getScene().init();

        while (true) {
            try {
                final var timestamp = System.currentTimeMillis();
                if (lastTimestamp == 0L) {
                    lastTimestamp = timestamp;
                } else {
                    this.getScene().update(1);
                    this.frame.getContentPane().invalidate();
                    if (timestamp - lastTimestamp > 10) {
                        //
                    } else {
                        Thread.sleep(5);
                    }
                }
            } catch (InterruptedException ex0) {
                return;
            } catch (Exception ex) {
                logger.error("Error found in cycle", ex);
            }
        }
    }

    class SceneAdaptor
            extends JComponent {
        public SceneAdaptor() {
        }

        @Override
        public boolean isLightweight() {
            return true;
        }

        @Override
        public void paint(
                Graphics g
        ) {
            BasicWorld.this.getScene().draw(g, this.getSize());
        }

        @Override
        public boolean isOpaque() {
            return true;
        }
    }
}
